package org.magnerfuller.imtoolkit.server.modules;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import org.magnerfuller.imtoolkit.server.modules.event.ModuleLoaderEvent;
import org.magnerfuller.imtoolkit.server.modules.event.listener.ModuleLoaderListener;
import org.magnerfuller.imtoolkit.util.StanzaFileParser;
import org.magnerfuller.imtoolkit.util.StanzaParse;

/**
 * 
 * The class ModuleLoader provides the ability to dynamically load
 * jar files into the classpath, notifying listeners of loading events
 * so that other actions can be taken once new classes and/or config
 * information become available.
 *
 */
public class ModuleLoader extends URLClassLoader {
  
  /** listeners -- the listeners to notify on events */
  private Vector<ModuleLoaderListener> listeners;

  /**
   * Default Constructor.
   */
  public ModuleLoader() {
    super(new URL[0]);

    listeners = new Vector<ModuleLoaderListener>();
  }//ModuleLoader
  
  /**
   * Load all jar files in a directory tree, processing config files as well.
   * 
   * @param file -- the root of the tree
   * @param propsFile -- the name of a properties file for configuration
   * @throws IOException
   * @throws Exception
   */
  public void loadModule(File file, String propsFile) throws IOException, Exception {
    
    if(file.isDirectory()) {
      System.err.println("Processing directory " + file.getPath());
      File[] entries = file.listFiles();
      for(int i = 0; i < entries.length; i++) {
        loadModule(entries[i], propsFile);
      }
    }
    else if(file.getPath().endsWith(".jar")) {
      System.err.println("Processing jar file " + file.getPath());
      JarFile jar = new JarFile(file);
      ZipEntry propsEntry = jar.getEntry(propsFile);
      StanzaParse props = null;
      
      if(propsEntry != null) {
        StanzaFileParser parser = new StanzaFileParser();
        props = parser.parseFile(jar.getInputStream(propsEntry));
      }
      
      URL url = new URL("file://" + file.getAbsolutePath());
      
      try { jar.close(); } catch (Exception e) {}
      
      addURL(url);
      
      ModuleLoaderEvent event;
      if(props != null) {
        event = new ModuleLoaderEvent(this, ModuleLoaderEvent.MODULE_WITH_PROPS_LOAD, props);
      } else {
        event = new ModuleLoaderEvent(this, ModuleLoaderEvent.MODULE_LOAD);
      }
      fireModuleLoaderEvent(event);
    } else {
      System.err.println("Ignoring non-module file " + file.getPath());
    }
  }//loadModule

  /**
   * Add a listener.
   * 
   * @param l -- the listener to add
   */
  public void addModuleLoaderListener(ModuleLoaderListener l) {
    listeners.addElement(l);
  }//addModuleListener
  
  /**
   * Remove a listener.
   * 
   * @param l -- the listener to remove
   * @return true on success, false otherwise
   */
  public boolean removeModuleLoaderListener(ModuleLoaderListener l) {
    return listeners.removeElement(l);
  }//removeModuleLoaderListener

  /**
   * Fire an event to all listeners.
   * 
   * @param e -- the event
   */
  protected void fireModuleLoaderEvent(ModuleLoaderEvent e) {
    for(ModuleLoaderListener l : listeners) {
      l.moduleLoaderEventOccurred(e);
    }
  }//fireModuleLoaderEvent
  
}//ModuleLoader
